﻿using System;
using System.Collections.Generic;

namespace ComTools.Queue.Implement
{
    /// <summary>
    /// 内存队列处理器，该类用于操作一个泛型数据队列对象，以队列的方式处理数据，包括基本的入队出队等操作
    /// </summary>
    /// <typeparam name="T">队列数据类型</typeparam>
    public class QueueProcessor<T> : Interface.IQueueProcessor<T>
    {
        #region 属性
        /// <summary>
        /// 数据的队列
        /// </summary>
        private readonly Queue<T> queue;

        /// <summary>
        /// 同步锁
        /// </summary>
        private readonly object synObject = new object();

        /// <summary>
        /// 队列处理器的状态（空闲、忙碌）
        /// </summary>
        private QueueProcessState state;

        /// <summary>
        /// 获取或设置每次处理的出队数据数量：默认为5
        /// </summary>        
        public int NumberToProcessPerDequeue { get; set; } = 5;

        /// <summary>
        /// 处理取出的队列数据的委托
        /// </summary>
        private Action<IList<T>> handler;

        /// <summary>
        /// 最后一次处理的时间
        /// </summary>
        private DateTime lastProcessDate = DateTime.MinValue;

        /// <summary>
        /// 自上次处理后过去的时间
        /// </summary>
        private TimeSpan elapsedTimeSinceLastProcessDate = TimeSpan.MinValue;

        /// <summary>
        /// 已经处理了的次数
        /// </summary>
        private int numberOfTimesProcessed = 0;

        /// <summary>
        /// 已经处理的数据总数
        /// </summary>
        private int totalProcessed { get; set; }

        /// <summary>
        /// 获取队列数据的总数
        /// </summary>
        /// <value>队列数据的总数</value>
        public int Count
        {
            get
            {
                lock (synObject)
                {
                    return queue.Count;
                }
            }
        }

        /// <summary>
        /// 是否空闲
        /// </summary>        
        public bool IsIdle
        {
            get
            {
                lock (synObject)
                {
                    return state == QueueProcessState.Idle;
                }
            }
        }

        /// <summary>
        /// 是否繁忙
        /// </summary>
        /// <value><c>true</c>繁忙;<c>false</c>空闲;</value>
        public bool IsBusy
        {
            get
            {
                lock (synObject)
                {
                    return state == QueueProcessState.Busy;
                }
            }
        }
        #endregion

        #region 构造函数

        /// <summary>
        /// 默认构造函数
        /// </summary>
        public QueueProcessor() : this(5, null)
        {
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="handler">处理数据的委托</param>
        public QueueProcessor(Action<IList<T>> handler) : this(5, handler)
        {
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="numberOfItemsToProcessPerDequeue">每次处理的出列的数据个数</param>
        /// <param name="handler">处理数据的委托</param>
        public QueueProcessor(int numberOfItemsToProcessPerDequeue, Action<IList<T>> handler)
        {
            queue = new Queue<T>();
            state = QueueProcessState.Idle;
            this.handler = handler;
            NumberToProcessPerDequeue = numberOfItemsToProcessPerDequeue;
        }

        #endregion

        #region 方法

        /// <summary>
        /// 入队
        /// </summary>
        /// <param name="item">入队项</param>
        public void Enqueue(T item)
        {
            lock (synObject)
            {
                queue.Enqueue(item);
            }
        }

        /// <summary>
        /// 出队
        /// </summary>
        /// <returns>从队列取出的数据</returns>
        public T Dequeue()
        {
            IList<T> items = Dequeue(1);
            if (items == null || items.Count == 0)
                return default(T);
            return items[0];
        }

        /// <summary>
        /// 从队列出队指定的数量
        /// </summary>
        /// <param name="numberToDequeue">出队数量</param>
        /// <returns>出队的项的集合</returns>
        public IList<T> Dequeue(int numberToDequeue)
        {
            IList<T> items = null;
            lock (synObject)
            {
                items = DequeueInternal(numberToDequeue);
            }
            return items;
        }

        /// <summary>
        /// 处理队列数据
        /// </summary>
        public virtual void Process()
        {
            IList<T> itemsToProcess = null;

            // 如果状态是繁忙，不做任何事情.
            if (IsBusy) { return; }

            lock (synObject)
            {
                if (Count == 0)
                {
                    return;
                }

                itemsToProcess = DequeueInternal(NumberToProcessPerDequeue);

                if (itemsToProcess == null) { return; }

                UpdateState(QueueProcessState.Busy, false);
            }

            UpdateMetaInfo(itemsToProcess.Count);

            Process(itemsToProcess);

            // 处理完后更新状态为空闲
            UpdateState(QueueProcessState.Idle, true);
        }

        /// <summary>
        /// 处理队列数据
        /// </summary>
        /// <param name="itemsToProcess">需要处理的队列数据</param>
        protected virtual void Process(IList<T> itemsToProcess)
        {
            try
            {
                handler?.Invoke(itemsToProcess);
            }
            catch (Exception ex)
            {
                throw new Exception("处理队列数据时出错，请检查日志:"+ ex.ToString());
            }
        }

        /// <summary>
        /// 出队
        /// </summary>
        /// <param name="numberToDequeue">出队的数据数量</param>
        /// <returns>出队的数据集合</returns>
        protected IList<T> DequeueInternal(int numberToDequeue)
        {
            if (queue.Count == 0)
                return null;

            IList<T> itemsToDeque = new List<T>();

            // 如果要取得数量大于队列现有数量，则把队列里的取完
            numberToDequeue = numberToDequeue > queue.Count ? queue.Count : numberToDequeue;

            for (var count = 1; count <= numberToDequeue; count++)
            {
                itemsToDeque.Add(queue.Dequeue());
            }

            return itemsToDeque;
        }

        /// <summary>
        /// 获取队列的状态
        /// </summary>
        /// <returns>队列的状态</returns>
        public virtual QueueStatus GetStatus()
        {
            QueueStatus status = null;
            lock (synObject)
            {
                status = new QueueStatus(
                    state,
                    queue.Count,
                    lastProcessDate,
                    numberOfTimesProcessed,
                    elapsedTimeSinceLastProcessDate,
                    totalProcessed,
                    NumberToProcessPerDequeue);
            }
            return status;
        }

        #endregion

        /// <summary>
        /// 更新当前队列处理器的状态
        /// </summary>
        /// <param name="newState">新状态</param>
        /// <param name="performLock">执行状态更新时是否加锁</param>
        protected void UpdateState(QueueProcessState newState, bool performLock)
        {
            if (performLock)
            {
                lock (synObject)
                {
                    state = newState;
                }
            }
            else
            {
                state = newState;
            }
        }

        /// <summary>
        /// 更新对象的元数据（包含一些当前处理状态的数据）
        /// </summary>
        /// <param name="numberBeingProcessed">处理的队列数据数量</param>
        private void UpdateMetaInfo(int numberBeingProcessed)
        {
            var now = DateTime.Now;

            // 设置最后处理时间及最后处理到当前处理时间的间隔时间
            if (lastProcessDate != DateTime.MinValue)
            {
                elapsedTimeSinceLastProcessDate = now.TimeOfDay - lastProcessDate.TimeOfDay;
            }
            lastProcessDate = now;

            // 累加处理次数
            System.Threading.Interlocked.Increment(ref numberOfTimesProcessed);

            // 累加处理的队列数据数量
            totalProcessed += numberBeingProcessed;
        }
    }
}
